package com.ruoyi.common.utils.http;

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.exception.ServiceException;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.entity.mime.FileBody;
import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
import org.apache.hc.client5.http.entity.mime.StringBody;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.*;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Http5Util {
    /**
     * 发起GET请求
     *
     * @param uri
     * @return
     * @throws IOException
     * @throws ParseException
     */
    public static String get(String uri, Header... headers) throws IOException, ParseException {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(uri);
            httpGet.setHeaders(headers);
            // 底层HTTP连接仍然由响应对象持有，
            // 以允许响应内容直接从网络套接字流。
            // 为了确保系统资源的正确释放，
            // 用户必须在finally子句中调用CloseableHttpResponse#close()。
            // 请注意，如果响应内容没有被完全使用，则底层连接将不能被安全重用，
            // 并将被连接管理器关闭和丢弃。
            return execute(httpclient, httpGet);
        }
    }

    public static <T> T get(String uri, Class<T> clazz, Header... headers) throws IOException, ParseException {
        String result = get(uri, headers);
        return JSONObject.parseObject(result, clazz);
    }

    /**
     * 发起带参GET请求
     *
     * @param uri
     * @param params 请求参数
     * @return
     * @throws IOException
     * @throws ParseException
     */
    public static String get(String uri, Map<String, Object> params, Header... headers) throws IOException, ParseException {
        if (params.size() > 0) {
            List<String> paramsList = new ArrayList<>();
            params.forEach((name, value) -> paramsList.add(String.format("%s=%s", name, value)));
            String paramsString = StringUtils.join(paramsList, "&");
            String fullUri = (uri.contains("?") ? "&" : "?") + paramsString;
            return get(fullUri);
        }
        return get(uri, headers);
    }

    /**
     * @param uri
     * @param params
     * @param clazz
     * @param headers
     * @param <T>
     * @return
     * @throws IOException
     * @throws ParseException
     */
    public static <T> T get(String uri, Map<String, Object> params, Class<T> clazz, Header... headers) throws IOException, ParseException {
        String result = get(uri, params, headers);
        return JSONObject.parseObject(result, clazz);
    }

    /**
     * 发起POST请求
     *
     * @param uri
     * @param nvps 请求参数
     * @return
     * @throws IOException
     * @throws ParseException
     */
    public static String post(String uri, List<NameValuePair> nvps, Header... headers) throws IOException, ParseException {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(uri);
            httpPost.setHeaders(headers);
            httpPost.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8));
            return execute(httpclient, httpPost);
        }
    }

    /**
     * 发起POST请求
     *
     * @param uri
     * @param body 请求参数
     * @return
     * @throws IOException
     * @throws ParseException
     */
    public static String post(String uri, Map<String, Object> body, Header... headers) throws IOException, ParseException {
        List<NameValuePair> nvps = new ArrayList<>();
        if (body != null) {
            body.forEach((name, value) -> nvps.add(new BasicNameValuePair(name, String.valueOf(value))));
        }
        return post(uri, nvps, headers);
    }

    public static String postStr(String uri, Map<String, String> body, Header... headers) throws IOException, ParseException {
        List<NameValuePair> nvps = new ArrayList<>();
        if (body != null) {
            body.forEach((name, value) -> nvps.add(new BasicNameValuePair(name, String.valueOf(value))));
        }
        return post(uri, nvps, headers);
    }


    /**
     * 发起POST请求
     *
     * @param uri
     * @param body  请求参数
     * @param clazz
     * @param <T>
     * @return
     * @throws IOException
     * @throws ParseException
     */
    public static <T> T post(String uri, Map<String, Object> body, Class<T> clazz, Header... headers) throws IOException, ParseException {
        String result = post(uri, body, headers);
        return JSONObject.parseObject(result, clazz);
    }

    /**
     * 发起POST请求
     *
     * @param uri
     * @param body 请求参数，可是是JSON字符串
     * @return
     * @throws IOException
     * @throws ParseException
     */
    public static String post(String uri, String body, Header... headers) throws IOException, ParseException {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(uri);
            httpPost.setHeaders(headers);
            httpPost.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
            return execute(httpclient, httpPost);
        }
    }

    /**
     * 发起POST请求
     *
     * @param uri
     * @param body  请求参数，可是是JSON字符串
     * @param clazz
     * @param <T>
     * @return
     * @throws IOException
     * @throws ParseException
     */
    public static <T> T post(String uri, String body, Class<T> clazz, Header... headers) throws IOException, ParseException {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(uri);
            httpPost.setHeaders(headers);
            httpPost.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
            String result = execute(httpclient, httpPost);
            return JSONObject.parseObject(result, clazz);
        }
    }

    public static <T> T upload(String uri, String fileName, String filePath, Map<String, String> body, Class<T> clazz, Header... headers) throws IOException, ParseException {
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(uri);

            FileBody bin = new FileBody(new File(filePath));

            final MultipartEntityBuilder[] builder = {MultipartEntityBuilder.create()
                    .addPart(fileName, bin)};
            if (body != null) {
                body.forEach((key, value) -> {
                    StringBody stringBody = new StringBody(value, ContentType.TEXT_PLAIN);
                    builder[0] = builder[0].addPart(key, stringBody);
                });
            }
            httpPost.setHeaders(headers);
            HttpEntity reqEntity = builder[0].build();
            httpPost.setEntity(reqEntity);
            String result = execute(httpclient, httpPost);
            return JSONObject.parseObject(result, clazz);
        }
    }

    /**
     * 执行请求
     *
     * @param httpclient
     * @param request
     * @return
     * @throws IOException
     * @throws ParseException
     */
    private static String execute(CloseableHttpClient httpclient, ClassicHttpRequest request) throws IOException, ParseException {
        // 底层HTTP连接仍然由响应对象持有，
        // 以允许响应内容直接从网络套接字流。
        // 为了确保系统资源的正确释放，
        // 用户必须在finally子句中调用CloseableHttpResponse#close()。
        // 请注意，如果响应内容没有被完全使用，则底层连接将不能被安全重用，
        // 并将被连接管理器关闭和丢弃。
        try (CloseableHttpResponse response = httpclient.execute(request)) {
            int code = response.getCode();
            if (code != 200) {
                throw new ServiceException("code：" + code + " " + response.getReasonPhrase() + (response.getEntity() == null ? "" : EntityUtils.toString(response.getEntity())));
            }
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
            // 对响应体做一些有用的事情
            // 并确保它被充分利用
            EntityUtils.consume(entity);
            return result;
        }
    }
}
